package info.motteke.mixivoice4j;

import java.util.ArrayList;
import java.util.List;

/**
 * {@linkplain Entry}を作成するためのクラスです。
 *
 * @author Y.Wakamatsu
 *
 */
public class EntryBuilder {

    /** キャッシュです。 */
    private final MixiVoiceCache cache;

    /** エントリの更新を監視するオブジェクトです。 */
    private final MixiVoiceObserver observer;

    /** コメントを作成するクラスを保存します。 */
    private final List<CommentBuilder> commentBuilders = new ArrayList<CommentBuilder>();

    /** コメントです。 */
    private final Comments comments;

    /** イイネ！をつけたユーザです。 */
    private final Favorite favorites;

    private boolean favoriteUpdated;

    /** 投稿IDです。 */
    private long id;

    /** 投稿者です。 */
    private User owner;

    /** 本文です。 */
    private Body body;

    /** 投稿画像のサムネイルのURLです。 */
    private ImageUrl thumbnail;

    /** 投稿画像のURLです。 */
    private ImageUrl image;

    /**
     * コンストラクタです。
     * このコンストラクタでは、ベースになるエントリが既に作成されていることを前提にします。
     *
     * @param cache
     *            キャッシュ
     * @param entry
     *            基になるエントリ
     */
    /* package */EntryBuilder(MixiVoiceCache cache, Entry entry, MixiVoiceObserver observer) {
        this.cache = cache;
        this.observer = observer;
        this.id = entry.getId();
        this.owner = entry.getUser();
        this.body = entry.getBody();
        this.favorites = entry.getFavorite();
        this.comments = entry.getComments();
        this.thumbnail = entry.getImageThumbnailUrl();
        this.image = entry.getImageUrl();
    }

    /**
     * コンストラクタです。
     *
     * @param cache
     *            キャッシュ
     */
    /* package */EntryBuilder(MixiVoiceCache cache, MixiVoiceObserver observer) {
        this.cache = cache;
        this.observer = observer;
        this.favorites = new Favorite();
        this.comments = new Comments();
        this.thumbnail = null;
        this.image = null;
    }

    /**
     * 作成中のエントリにコメントを追加します。
     * @return
     */
    public CommentBuilder addComment() {
        CommentBuilder cb = new CommentBuilder(cache, observer);
        commentBuilders.add(cb);

        return cb;
    }

    public EntryBuilder identifiedBy(long id, User user) {
        this.id = id;
        this.owner = user;

        return this;
    }

    public EntryBuilder body(Body body) {
        this.body = body;

        return this;
    }

    public EntryBuilder image(ImageUrl thumbnail, ImageUrl image) {
        this.thumbnail = thumbnail;
        this.image = image;

        return this;
    }

    public EntryBuilder favorite(User user) {
        boolean updated = favorites.add(user);
        favoriteUpdated = favoriteUpdated | updated;

        return this;
    }

    /**
     * エントリを作成します。
     *
     * @return エントリ
     */
    public Entry build() {
        Entry entry = cache.getVoice(id, owner, Entry.class);
        boolean newEntry = false;

        // キャッシュになければ新規作成
        if (entry == null) {
            entry = new Entry(this);

            // 新規エントリの設定
            newEntry = true;
            observer.addNewVoice(entry);
            cache.addVoice(entry);
        }

        // コメントの作成
        for (CommentBuilder cb : commentBuilders) {
            // コメントによる更新はコメント側に任せる。
            comments.add(cb.build(entry));
        }

        // イイネ！の更新は漸次行われるため、ここではオブザーバへの登録のみ
        if (favoriteUpdated) {
            observer.addFavoriteUpdatedEntry(entry);
        }

        // 全ての更新を設定する。
        boolean updated = newEntry | favoriteUpdated;
        if (updated) {
            observer.addUpdatedEntry(entry);
        }

        return entry;
    }

    /* package */Favorite getFavorites() {
        return favorites;
    }

    /* package */Comments getComments() {
        return comments;
    }

    /* package */long getId() {
        return id;
    }

    /* package */User getUser() {
        return owner;
    }

    /* package */Body getBody() {
        return body;
    }

    /* package */ImageUrl getThumbnail() {
        return thumbnail;
    }

    /* package */ImageUrl getImage() {
        return image;
    }
}
